home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / Samples / Moofwars 1.02 / MoofWars Sprocket / •Source / TTileGrid.cp < prev   
Encoding:
Text File  |  1998-08-10  |  11.8 KB  |  394 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************
  2. TTileGrid.cp
  3.  
  4. A TileGrid is a class that describes a 2 dimensional array of tiles that can be 
  5. drawn as the background for a game.  This current version only supplies
  6. graphics routines, but collision and other logic should be fairly easy to
  7. generate.
  8.  
  9. Author: Timothy Carroll
  10. Apple Developer Technical Support
  11. timc@apple.com
  12.  
  13. Modification History: 
  14.  
  15. 4/2/97        TMC        fTileData is a handle, but the destructor was calling DisposePtr on it.
  16. Ouch!  Bad programmer, no donut.  Because my handle was cast to a **CellGridType, I had
  17. to recast it back when I disposed of it, and I did it wrong.  Spotlight caught this
  18. one for me.
  19. 1/23/97     TMC        Added include for Moofwars.h so that MrC will compile
  20. 8/15/96        TMC     Initial Release
  21.  
  22. Copyright © 1996, 1997 Apple Computer, Inc., All Rights Reserved
  23.  
  24. You may incorporate this sample code into your applications without
  25. restriction, though the sample code has been provided "AS IS" and the
  26. responsibility for its operation is 100% yours.  However, what you are
  27. not permitted to do is to redistribute the source as "DSC Sample Code"
  28. after having made changes. If you're going to re-distribute the source,
  29. we require that you make it clear in the source that the code was
  30. descended from Apple Sample Code, but that you've made changes.
  31. *************************************************************************************/
  32. #include <Memory.h>
  33. #include <Resources.h>
  34. #include "Moofwars.h"
  35. #include "TTileGrid.h"
  36. #include "scaling.h"
  37.  
  38.  
  39.  
  40.  
  41. /*************************************************************************************
  42. TTileGrid::TTileGrid
  43. ~TTileGrid::TTileGrid
  44. *************************************************************************************/
  45.  
  46. TTileGrid::TTileGrid (void)
  47. {    
  48.     fWidth = 0;
  49.     fHeight = 0;
  50.     fDefaultTile = 0;
  51.     fTiles = NULL;
  52.     fTileData = NULL;
  53.     
  54. }
  55.  
  56. TTileGrid::~TTileGrid (void)
  57. {
  58.     if (fTiles != NULL)
  59.         DisposeTileCollection (fTiles);
  60.     if (fTileData != NULL)
  61.         DisposeHandle ((Handle) fTileData);
  62. }
  63.  
  64. /*************************************************************************************
  65. TTileGrid::CreateGridFromResource
  66. *************************************************************************************/
  67. OSErr
  68. TTileGrid::CreateGridFromResource (SInt16 resID)
  69. {
  70.     OSErr                theErr = noErr;
  71.     TileGridResHeader    **grid;
  72.     Ptr                    srcPtr, destPtr;
  73.     
  74.     grid = (TileGridResHeader **) Get1Resource (TileGridResType, resID);
  75.     theErr = ResError();
  76.     FAIL_OSERR (theErr,"\pFailed to load GRID resource")
  77.     FAIL_NIL (grid, "\pFailed to load GRID resource")
  78.     
  79.     if ((**grid).version != 0) SIGNAL_ERROR ("\pUnable to read this GRID format")
  80.     
  81.     fWidth = (**grid).width;
  82.     fHeight = (**grid).height;
  83.     fDefaultTile = (**grid).defaultTile;
  84.     fTiles = NewTileCollection ((**grid).tileResID);
  85.     FAIL_NIL (fTiles, "\pFailed to load the Tiles needed for GRID.")
  86.     fTiles->LockCollection();
  87.     
  88.     fTileData = (CellGridType **) NewHandle (fWidth * fHeight * sizeof (CellGridType));
  89.     theErr = MemError();
  90.     FAIL_OSERR (theErr, "\pFailed to allocate memory for the GRID data")
  91.     FAIL_NIL (fTileData, "\pFailed to allocate memory for the GRID data")
  92.  
  93.     destPtr = (Ptr) *fTileData;
  94.     srcPtr = (Ptr) (*grid)+sizeof (TileGridResHeader);
  95.     
  96.     BlockMoveData (srcPtr, destPtr, fWidth * fHeight * sizeof (CellGridType));
  97.     
  98.     goto cleanup;
  99.     
  100.     error:
  101.     
  102.     if (theErr == noErr)
  103.         theErr = paramErr;
  104.     
  105.     cleanup:
  106.     
  107.     if (grid != NULL)
  108.         ReleaseResource ((Handle) grid);
  109.     
  110.     return theErr;
  111.  
  112. }
  113.  
  114.  
  115. /*************************************************************************************
  116. Accessor Functions
  117. *************************************************************************************/
  118.  
  119. CellGridType
  120. TTileGrid::GetGridValue (SInt32 h, SInt32 v)
  121. {
  122.     SInt32 offset;
  123.     if ((h < 0) || (h >= fWidth) || (v < 0) || (v >= fHeight))
  124.         return fDefaultTile;
  125.     
  126.     offset = (v*fHeight + h);
  127.     return *((*fTileData)+offset);
  128. }
  129.  
  130. void
  131. TTileGrid::SetGridValue (SInt32 h, SInt32 v, CellGridType value)
  132. {
  133.     SInt32 offset;
  134.     
  135.     if ((h < 0) || (h >= fWidth) || (v < 0) || (v >= fHeight))
  136.         return;
  137.         
  138.     offset = (v*fHeight + h);
  139.     
  140.     *((*fTileData)+offset) = value;
  141. }
  142.  
  143. /*************************************************************************************
  144. TTileGrid::DrawGrid
  145.  
  146. The heart of the current TTileGrid code.  This routine uses the values stored in the grid to draw
  147. into a rectangle on the screen.
  148.  
  149. Inputs:
  150.  
  151. We input a rectangle (in screen coordinates) to draw to, and the top left corner (in world coordinates)
  152. that corresponds to the top left corner of the rectangle.  This routine figures out which tiles need to be
  153. drawn and draws them as quickly as possible.
  154.  
  155.  
  156. Further optimizations:
  157. In our worse case, we clip both vertically and horizontally.  A normal unclipped tile grid would be 20x15
  158. for a 640x480 screen, or 300 tiles.  The fully clipped case would be 21x16 or 336 tiles.  Of these tiles,
  159. 70 of them will be drawn with the clipped algorithm or about 20%.  So speeding up the handling of clipped
  160. tiles would improve the speed of grid drawing in most cases.
  161.  
  162. The best way to speed up clipping is probably to differentiate between vertical and horizontal clipping.
  163. If we are just vertical clipping, we can use the same fast inner blit loops that we've been using for
  164. the unclipped blitters -- we just draw less rows.  Horizontal clipping can use the fact that we always
  165. have a set of data aligned to the same alignment as the destination.
  166.  
  167. As mentioned below, there is one switch that can be moved from once per tile to once per grid.  The main
  168. reason this isn't done in the code below is that I haven't found a clean C++ way to do it that doesn't 
  169. involve copying the same loop 8 times.  I figured that clarity was better in this case, because the
  170. performance difference was negligible.
  171. *************************************************************************************/
  172.  
  173. void
  174. TTileGrid::DrawGrid (Rect *screenRect, SInt32 topGlobal, SInt32 leftGlobal)
  175. {
  176.     SInt32    tilesWidth, tilesHeight;        // number of tiles in each direction to draw.
  177.     SInt32    indexH, indexV;                 // index coordinates of the tile currently being drawn
  178.     SInt32    startH;                            // base index to start each line at
  179.     SInt32    drawY, drawX;                    // screen coordinates of the tile currently being drawn
  180.     SInt32    startX;                            // base x coordinate to start each line of tiles at.
  181.     Boolean    horizontalClip, verticalClip;    // whether or not we need to clip
  182.     
  183.     SInt32    hLoop, vLoop;                      // used to loop over the tiles being drawn
  184.     
  185.     unsigned char    *destPtr;                // Points to the destination inside the buffer
  186.     unsigned char    *destRowPtr;            // Points to the beginning of the line in a buffer
  187.     UInt32            vertOffset;                // Use this to increment destRowPtr to the next line of tiles
  188.     UInt32            alignment;                // we can calculate the alignment for all the tiles, which
  189.                                             // is their exact byte alignment.  This lets us call a custom
  190.                                             // blitter for that alignment.  See the TTileCollection
  191.                                             // blitters for more info.
  192.     
  193.     SInt32            temp;
  194.  
  195.  
  196. // ASSERTIONS
  197. // The screenRectangle should start on a multiple of 8 bytes, and should be an integral number of tiles
  198. // tall and wide.  This helps us determine exactly which tiles to draw.
  199.  
  200. #if qDebugging
  201.     if (screenRect->left % 8 != 0)
  202.     {
  203.         DebugStr ("\pscreenRect improperly aligned");
  204.         return;
  205.     }
  206.     
  207.     if ((screenRect->right - screenRect->left) % 32 != 0)
  208.     {
  209.         DebugStr ("\pscreenRect improper width");
  210.         return;
  211.     }    
  212.     
  213.     if ((screenRect->bottom - screenRect->top) % 32 != 0)
  214.     {
  215.         DebugStr ("\pscreenRect improper width");
  216.         return;
  217.     }    
  218. #endif
  219.  
  220. // First, determine the number of tiles in the screen rectangle.
  221.     
  222.     tilesWidth = (screenRect->right - screenRect->left) >> 5;
  223.     tilesHeight = (screenRect->bottom - screenRect->top) >> 5;
  224.  
  225. // We need to determine where we actually need to start drawing, both in screen coordinates and in
  226. // indices into the Grid values.  We also determine if we actually need to perform any clipping.
  227. // 
  228. // If the top/left coordinate in world coordinates is a multiple of 32, then we are exactly aligned to
  229. // the screen rectangle, and no clipping needs to take place.  This is actually the fastest case, because
  230. // we don't call our slower clipping tiles, and we also call the fastest tile blitter.
  231. // 
  232. // If it isn't a multiple of 32, then we're not aligned and we need to draw an extra tile to get the
  233. // full row.  Because we handle the clipped case separately, we actually decrement the number of tiles
  234. // we'll be drawing.
  235.  
  236.     temp = leftGlobal % 32;
  237.     startH = leftGlobal / 32;
  238.     if (temp != 0)
  239.     {
  240.         tilesWidth--;
  241.         horizontalClip = true;
  242.         
  243.         if (leftGlobal < 0)
  244.         {
  245.             startH = (leftGlobal-31) / 32;
  246.             if (temp < 0) temp+=32;
  247.         }
  248.     }
  249.     else
  250.         horizontalClip = false;
  251.  
  252. // However much our start point is offset to the right, we need to offset that far to the left to
  253. // determine our initial drawing point.
  254.     startX = screenRect->left - temp;
  255.     
  256.  
  257. // We do the same for the vertical components.
  258.  
  259.     temp = topGlobal % 32;
  260.     indexV = topGlobal / 32;
  261.     if (temp != 0)
  262.     {
  263.         tilesHeight--;
  264.         verticalClip = true;
  265.         
  266.         if (topGlobal < 0)
  267.         {
  268.             indexV = (topGlobal-31) /32;
  269.             if (temp < 0) temp+= 32;
  270.         }
  271.     }
  272.     else
  273.         verticalClip = false;
  274.     
  275.     drawY =  screenRect->top - temp;
  276.         
  277.     // Calculate our initial destination pointer and our offset between rows.  We also
  278.     // calculate the alignment for the entire grid.
  279.  
  280.     destRowPtr = gDestBaseAddr + drawY * gRowBytes + startX;
  281.     vertOffset = gRowBytes << 5;
  282.     alignment = ((long) destRowPtr) & 0x07;
  283.     
  284.     // handle top clip
  285.     if (verticalClip)
  286.     {
  287.         drawX = startX;
  288.         indexH = startH;
  289.         
  290.         if (horizontalClip)
  291.             temp = tilesWidth+2;
  292.         else
  293.             temp = tilesWidth;
  294.             
  295.         for (hLoop = 0; hLoop < temp; hLoop++)
  296.         {
  297.             UInt32 index;
  298.             
  299.             index = GetGridValue (indexH, indexV);
  300.             fTiles->CopyImageClipped (index, drawY, drawX);
  301.             indexH++;
  302.             drawX +=32;
  303.         }
  304.         
  305.         indexV++;
  306.         drawY += 32;
  307.         destRowPtr += vertOffset;
  308.     }
  309.  
  310.  
  311.     for (vLoop = 0; vLoop < tilesHeight; vLoop++)
  312.     {
  313.         
  314.         drawX = startX;
  315.         indexH = startH;
  316.         destPtr = destRowPtr;
  317.     
  318.         if (horizontalClip)
  319.         {
  320.             UInt32 index;
  321.             
  322.             index = GetGridValue (indexH, indexV);
  323.             fTiles->CopyImageClipped (index, drawY, drawX);
  324.             indexH++;
  325.             drawX +=32;
  326.             destPtr += 32;
  327.         }
  328.         
  329.         for (hLoop = 0; hLoop < tilesWidth; hLoop++)
  330.         {
  331.             UInt32 index;
  332.             
  333.             index = GetGridValue (indexH, indexV);
  334.             
  335.             // *********** NOTE: *************
  336.             // Alignment is actually the same for all tiles in the grid, which means we could
  337.             // optimize this switch outside all the loops.  Only problem with this is that we
  338.             // end up copying a lot of loop code for each alignment.  I had this in the code and
  339.             // took it out for this sample because the function had grown huge, but it is something
  340.             // that can be optimized further.  In a C implementation, you could probably get a function
  341.             // pointer to the appropriate blitter, once, outside the loop and call from inside.
  342.             switch (alignment)
  343.             {
  344.                 case 0: fTiles->CopyImageUnclipped0 (index, destPtr); break;
  345.                 case 1: fTiles->CopyImageUnclipped1 (index, destPtr); break;
  346.                 case 2: fTiles->CopyImageUnclipped2 (index, destPtr); break;
  347.                 case 3: fTiles->CopyImageUnclipped3 (index, destPtr); break;
  348.                 case 4: fTiles->CopyImageUnclipped4 (index, destPtr); break;
  349.                 case 5: fTiles->CopyImageUnclipped5 (index, destPtr); break;
  350.                 case 6: fTiles->CopyImageUnclipped6 (index, destPtr); break;
  351.                 case 7: fTiles->CopyImageUnclipped7 (index, destPtr); break;
  352.             }
  353.             indexH++;
  354.             drawX +=32;
  355.             destPtr += 32;
  356.         }
  357.         
  358.         if (horizontalClip)
  359.         {
  360.             UInt32 index;
  361.             
  362.             index = GetGridValue (indexH, indexV);
  363.             fTiles->CopyImageClipped (index, drawY, drawX);
  364.         }
  365.         
  366.         indexV++;
  367.         drawY += 32;
  368.         destRowPtr += vertOffset;
  369.     }
  370.     
  371.     // handle bottom clip
  372.     if (verticalClip)
  373.     {
  374.         SInt32 temp;
  375.         
  376.         drawX = startX;
  377.         indexH = startH;
  378.         
  379.         if (horizontalClip)
  380.             temp = tilesWidth+2;
  381.         else
  382.             temp = tilesWidth;
  383.             
  384.         for (hLoop = 0; hLoop < temp; hLoop++)
  385.         {
  386.             UInt32 index;
  387.             
  388.             index = GetGridValue (indexH, indexV);
  389.             fTiles->CopyImageClipped (index, drawY, drawX);
  390.             indexH++;
  391.             drawX +=32;
  392.         }
  393.     }
  394. }